package model;

import java.util.LinkedList;

import main.Simulator;
import main.Util;
import event.Event;
import event.PacketArrivedAtRouterEvent;
import event.PacketReceivedByReceiverEvent;
import event.PacketSentByRouterEvent;

public abstract class Router {

	Simulator simulator;
	LinkedList<Packet> buffer;
	int bufferCapacity;
	double transmissionTime;
	
	/**
	 * Classe representando a entidade Router do TCP.
	 * Note que est� � uma classe abstrata e, assim, quem a extende
	 * deve implementar o tipo de gargalo definido atrav�s do m�todo
	 * packetArrived.
	 * @param simulator simulador 
	 * @param bufferCapacity capacidade m�xima do buffer em n�mero de pacotes
	 */
	public Router(Simulator simulator, int bufferCapacity) {
		this.simulator = simulator;
		this.buffer = new LinkedList<Packet>();
		this.bufferCapacity = bufferCapacity;
		this.transmissionTime = Util.convertSecondsToMillis((8.0 * simulator.getProperties().getMSS() / simulator.getProperties().getCg()));
	}
	
	/**
	 * M�todo que define o tipo de gargalo implementado pelo router e � chamado
	 * pelo simulador ao processar o evento {@link PacketArrivedAtRouterEvent}.
	 */
	public abstract void packetArrived(Packet pack);
	
	/**
	 * M�todo utilizado para inserir de fato um pacote recebido pelo router em seu
	 * buffer. Este m�todo � chamado pelo packedArrived.
	 * @param pack
	 */
	public void addPacket(Packet pack) {
		if (buffer.size() >=  bufferCapacity) {
			throw new IllegalStateException("Buffer estorou.");
		}
		
		// Se o buffer estiver vazio, programa a primeira sa�da do buffer.
		if (buffer.isEmpty()) {
			double time = simulator.getTime() + transmissionTime;
			Event event = new PacketSentByRouterEvent(time, pack);
			simulator.addEvent(event);
		}
		buffer.add(pack);
	}
	
	/**
	 * M�todo que programa o evento de chegada no receptor.
	 * @param pack
	 */
	public void sendPacket(Packet pack) {
		if (pack != buffer.remove()) {
			throw new IllegalStateException("Pacote indevidamente removido.");
		}
		
		// Caso seja um pacote do tr�fego de fundo, este deve ser ignorado
		if (!(pack instanceof BackgroundPacket)) {
			Event event = new PacketReceivedByReceiverEvent(simulator.getTime(), pack);
			simulator.addEvent(event);
		}
		
		// Programa a pr�xima sa�da do pacote do buffer.
		if (!buffer.isEmpty()) {
			double time = simulator.getTime() + transmissionTime;
			Event event = new PacketSentByRouterEvent(time, buffer.element());
			simulator.addEvent(event);
		}
	}
	
	public int getBufferSize() {
		return this.buffer.size();
	}
	
}
